#pragma once
#include <iostream>
#include <memory>
#include <sys/select.h>
#include "Log.hpp"
#include "Socket.hpp"

using namespace Net_Work;
const static int g_defaultport = 8888;
const static int g_backlog = 16;
const static int num = sizeof(fd_set) * 8; // select托管文件描述符的上限

class SelectServer
{
private:
    void HandlerEvent(fd_set &fds)
    {
        for (int i = 0; i < num; i++)
        {
            if (_fds_array[i] == nullptr) //  1.判断是否合法
                continue;
            int fd = _fds_array[i]->GetSockfd();
            if (FD_ISSET(fd, &fds)) //  2.判断是否就绪
            {
                if (fd == _listensock->GetSockfd()) // 3.判断是否是监听或客户端套接字就绪
                {
                    lg.LogMessage(Info, "get a new link\n");
                    std::string clientip;
                    uint16_t clientport;
                    // accept不会阻塞！
                    Socket *sock = _listensock->AcceptConnection(&clientip, &clientport);
                    if (!sock)
                    {
                        lg.LogMessage(Error, "accept error\n");
                        return;
                    }
                    lg.LogMessage(Info, "get a client, client info# %s, %d, fd: %d\n", clientip.c_str(), clientport, sock->GetSockfd());

                    // 获取到一个链接了，可以直接进行read，write了吗？肯定不能
                    // 我们还不知道数据有没有就绪，只有select清楚数据有没有就绪
                    // 新链接fd来的时候，需要交给select托管起来，使用一个数组管理起来！

                    int pos = 0;
                    for (; pos < num; pos++)
                    {
                        if (_fds_array[pos] == nullptr)
                        {
                            _fds_array[pos] = sock;
                            lg.LogMessage(Info, "get a new link, fd is : %d\n", sock->GetSockfd());
                            break;
                        }
                    }
                    if (pos == num)
                    {
                        sock->CloseSocket();
                        delete sock;
                        lg.LogMessage(Error, "fds_array is full...\n");
                    }
                }
                else // 普通的读事件
                {
                    std::string buffer;
                    bool res = _fds_array[i]->Recv(&buffer, 1024);
                    if (res)
                    {
                        lg.LogMessage(Info, "client say# %s\n", buffer.c_str());
                        buffer += ": 你好我是服务器\n";
                        _fds_array[i]->Send(buffer);
                        buffer.clear();
                    }
                    else
                    {
                        lg.LogMessage(Warning, "client quit, maybe close or error, close fd : %d\n", _fds_array[i]->GetSockfd());
                        _fds_array[i]->CloseSocket();
                        delete _fds_array[i];
                        _fds_array[i] = nullptr;
                    }
                }
            }
        }
    }

public:
    SelectServer(int port = g_defaultport)
        : _listensock(new TcpSocket()), _port(port), _isrunning(false)
    {
    }

    void Init()
    {
        _listensock->BuildListensocketMethod(_port, backlog);
        for (int i = 0; i < num; i++)
        {
            _fds_array[i] = nullptr;
        }
        _fds_array[0] = _listensock.get();
    }

    void Loop()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // fd_set放在循环内
            fd_set fds;
            FD_ZERO(&fds);
            int max_fd = _fds_array[0]->GetSockfd();
            for (int i = 0; i < num; i++)
            {
                if (_fds_array[i] == nullptr)
                {
                    continue;
                }
                else
                {
                    int fd = _fds_array[i]->GetSockfd();
                    FD_SET(fd, &fds);
                    if (fd > max_fd)
                    {
                        max_fd = fd;
                    }
                }
            }

            PrintDebug();
            struct timeval timeout = {0, 0};
            int s = select(max_fd + 1, &fds, nullptr, nullptr, /*&timeout*/ nullptr);
            switch (s)
            {
            case 0:
                lg.LogMessage(Info, "select timeout..., last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
                break;
            case -1:
                lg.LogMessage(Error, "select error\n");
                break;
            default:
            {
                lg.LogMessage(Info, "select success, begin event handler, last time: %u.%u\n", timeout.tv_sec, timeout.tv_usec);
                HandlerEvent(fds);
                break;
            }
            }
        }
        _isrunning = false;
    }

    void PrintDebug()
    {
        std::cout << "current select list is ";
        for (int i = 0; i < num; i++)
        {
            if (_fds_array[i] == nullptr)
            {
                continue;
            }
            else
            {
                std::cout << _fds_array[i]->GetSockfd() << " ";
            }
        }
        std::cout << std::endl;
    }

private:
    std::unique_ptr<Socket> _listensock;
    int _port;
    bool _isrunning;

    Socket *_fds_array[num];
};
